#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import ConfigParser
import dircache
import Image
import ImageColor
import ImageDraw
import ImageFont
import os
import sys
import time

from db import db
from EbbDevice.GraphicLCD import GraphicLCD
from EbbDevice.LedMatrix import LedMatrix

class NetEbbms:
	def __init__(self, configfile, modelfile, showflag = False):
		# 設定ファイル読み込み #
		config = ConfigParser.SafeConfigParser()
		config.read(os.path.abspath(configfile))

		self.config_daemonLogDir = config.get('daemon', 'log_dir') or '/var/log/netebbms/'
		self.config_daemonRunDir = config.get('daemon', 'run_dir') or '/var/run/netebbms/'

		self.config_databaseHost = config.get('database', 'host') or 'localhost'
		self.config_databasePort = config.getint('database', 'port') or 3306
		self.config_databaseName = config.get('database', 'name') or 'netebbms'
		self.config_databaseUser = config.get('database', 'user') or 'root'
		self.config_databasePasswd = config.get('database', 'passwd') or ''

		self.config_hardwareModel = config.get('hardware', 'model')

		self.config_animationFileExtension = config.get('animation', 'file_extension') or 'ebb'
		self.config_animationCacheDir = config.get('animation', 'cache_dir') or '/var/cache/netebbms/animation_data/'
		self.config_animationHoldCount = config.getint('animation', 'hold_count') or 3


		# モデル リスト ファイル読み込み #
		modelList = ConfigParser.SafeConfigParser()
		modelList.read(os.path.abspath(modelfile))

		self.model_controller = modelList.get(self.config_hardwareModel, 'controller') or ''
		self.model_widthMM = modelList.getint(self.config_hardwareModel, 'width_mm') or 1
		self.model_heightMM = modelList.getint(self.config_hardwareModel, 'height_mm') or 1
		self.model_widthPixel = modelList.getint(self.config_hardwareModel, 'width_pixel') or 1
		self.model_heightPixel = modelList.getint(self.config_hardwareModel, 'height_pixel') or 1
		self.model_color = modelList.getint(self.config_hardwareModel, 'color') or 1


		# ディレクトリを作成 #
		try:
			if os.path.exists(self.config_daemonLogDir) == False:
				os.makedirs(self.config_daemonLogDir)

			if os.path.exists(self.config_daemonRunDir) == False:
				os.makedirs(self.config_daemonRunDir)

			if os.path.exists(self.config_animationCacheDir) == False:
				os.makedirs(self.config_animationCacheDir)

		except Exception, ex:
			print >> sys.stderr, ex
			sys.exit(1)


		# データベースに接続 #
		try:
			self.myDBMS = db(
				self.config_databaseHost,
				self.config_databasePort,
				self.config_databaseName,
				self.config_databaseUser,
				self.config_databasePasswd,
			)

			self.myDBMS.connect()

			if showflag == True:
				self.myDBMS.fetchAnimation(self.config_hardwareModel)

		except Exception, ex:
			print >> sys.stderr, ex
			sys.exit(1)


		# デバイスに接続 #
		try:
			if self.model_controller == GraphicLCD.__name__:
				self.myEBBDevice = GraphicLCD(7, 6, 5, 4, 0, 1, 2, 3)

			elif self.model_controller == LedMatrix.__name__:
				self.myEBBDevice = LedMatrix(6, 7, 0, 1, 2, 3, 4, 5)

			else:
				raise Exception('controller not found')

			if showflag == True:
				self.myEBBDevice.Begin(self.model_widthPixel, self.model_heightPixel)

		except Exception, ex: 
			print >> sys.stderr, ex
			self.myDBMS.close()
			sys.exit(1)

		self.cnt = 0  # need check!


	def close(self):
		"""モジュールを閉じる"""
		self.myEBBDevice.End()
		self.myDBMS.close()


	def initDBMS(self):
		"""データベースのテーブルを初期化"""
		self.myDBMS.init_database()


	def setText(self,
		generator = '',
		memo = '',
		mode = 'LOOP',
		enable = True,
		count = 1,
		end_time = 0.0,
		backcolor = 'white',
		textstyle = ()):
		"""テキストをデータベースに追加"""
		try:
			# テキストの最大長を探索 #
			textWidth = 0
			for style in textstyle:
				style_text = (style.get('text') or '').decode('utf-8')
				style_fontpath = style.get('fontpath') or ''
				style_color = style.get('color') or 'black'
				style_point = style.get('point') or 16
				style_scroll = style.get('scroll') or 0
				style_positionX = style.get('positionX') or 0
				style_positionY = style.get('positionY') or 0

				# 静止テキストの場合はテキスト最大長に含めない #
				if style_scroll == 0:
					continue

				# テキスト長を取得 #
				font = ImageFont.truetype(style_fontpath, style_point)
				width = ImageDraw.Draw(Image.new('RGB', (1, 1))).textsize(style_text, font)[0]

				# 最大テキスト長を更新 #
				if textWidth < width:
					textWidth = width

			# ファイルを開く #
			fileName = '%s%05d.%s' % (generator, self.cnt, self.config_animationFileExtension)
			file = open(os.path.join(self.config_animationCacheDir, fileName), 'wb')

			self.cnt += 1

			# テキストデータをビットマップに変換 #
			for frameCount, x in enumerate(xrange(self.model_widthPixel, textWidth * -1, -1)):
				img = Image.new('RGB', (self.model_widthPixel, self.model_heightPixel), backcolor)
				g = ImageDraw.Draw(img)

				for style in textstyle:
					style_text = (style.get('text') or '').decode('utf-8')
					style_fontpath = style.get('fontpath') or ''
					style_color = style.get('color') or 'black'
					style_point = style.get('point') or 16
					style_scroll = style.get('scroll') or 0
					style_positionX = style.get('positionX') or 0
					style_positionY = style.get('positionY') or 0

					font = ImageFont.truetype(style_fontpath, style_point)

					# テキストを描画 #
					if style_scroll == 0:
						g.text((style_positionX, style_positionY), style_text, style_color, font)

					else:
						g.text((x, style_positionY), style_text, style_color, font)

				# ファイルに出力 #
				self._fileWrite(file, img.tostring())
#				img.save('/tmp/Image/%02d-%04d.png' % (self.cnt, frameCount + 1))


			# ファイルを閉じる #
			file.close()

			# データベースに追加 #
			self.myDBMS.setAnimation(
				generator = generator,
				memo = memo,
				created_time = time.time(),
				model = self.config_hardwareModel,
				enable = True,
				mode = self._checkMode(mode),
				count = count,
				end_time = end_time,
				frame_count = frameCount + 1,
				frame_filename = fileName
			)

		except Exception, ex:
			print >> sys.stderr, ex
			self.close()
			sys.exit(1)


	def setImage(self,
		generator = '',
		memo = '',
		mode = 'LOOP',
		enable = True,
		count = 1,
		end_time = 0.0,
		backcolor = 'white',
		imagepath = '.',
		scroll = 1,
		positionX = 0,
		positionY = 0):
		"""画像をデータベースに追加"""
		try:
			# 対応する画像の拡張子リスト #
			valid_extension = ('bmp', 'gif', 'jpeg', 'jpg', 'png')

			# ファイルを開く #
			fileName = '%s%05d.%s' % (generator, self.cnt, self.config_animationFileExtension)
			file = open(os.path.join(self.config_animationCacheDir, fileName), 'wb')

			self.cnt += 1

			if os.path.isfile(imagepath):  # ファイルが指定された場合 #
				img = Image.open(imagepath).convert('RGB').resize((128, 64))
				imageWidth = img.size[0]

				for frameCount, x in enumerate(xrange(self.model_widthPixel, imageWidth * -1, -1)):
					campus = Image.new('RGB', (self.model_widthPixel, self.model_heightPixel), backcolor)

					if scroll == 0:  # スクロールしない場合 #
						campus.paste(img, (positionX, positionY))

					else:  # スクロールする場合 #
						campus.paste(img, (x, positionY))

					# ファイルに出力 #
					self._fileWrite(file, campus.tostring())
#					campus.save('/tmp/Image/%02d-%04d.png' % (self.cnt, frameCount + 1))

					if scroll == 0:  # スクロールしない場合ループから抜ける #
						break

			elif os.path.isdir(imagepath):  # ディレクトリが指定された場合 #
				for frameCount, fileName in enumerate(dircache.listdir(imagepath)):
					if os.path.splitext(fileName)[1].lstrip('.') in valid_extension:

						campus = Image.new('RGB', (self.model_widthPixel, self.model_heightPixel), backcolor)
						campus.paste(Image.open(os.path.join(dirPath, fileName)).convert('RGB'), (positionX, positionY))

						self._fileWrite(file, campus.tostring())
#						campus.save('/tmp/Image/%s-%02d-%04d.png' % (generator, self.cnt, frameCount + 1))

			else:  # 不正なパスが指定された場合 #
				print "path is not found!"
				return


			# ファイルを閉じる #
			file.close()

			# データベースに追加 #
			self.myDBMS.setAnimation(
				generator = generator,
				memo = memo,
				created_time = time.time(),
				model = self.config_hardwareModel,
				enable = True,
				mode = self._checkMode(mode),
				count = count,
				end_time = end_time,
#				frame_count = frameCount + 1,
				frame_count = 0,
				frame_filename = fileName
			)

		except Exception, ex:
			print >> sys.stderr, ex
			self.close()
			sys.exit(1)


	def showAnimation(self):
		try:
			# データベースから表示データ取得 #
			rowItem = self.myDBMS.getAnimationOne()
			while rowItem == None:
				self.myDBMS.fetchAnimation(self.config_hardwareModel)
				rowItem = self.myDBMS.getAnimationOne()
				print 'loop'

			# カラムごとに分割 #
			(animation_id, created_time, mode, count, end_time, frame_filename) = rowItem

			# ファイルパスを取得 #
			filePath = os.path.join(self.config_animationCacheDir, frame_filename)

			# ファイルの存在を確認 #
			if os.path.exists(filePath) == False:
				self.myDBMS.removeAnimationOne(animation_id)
				return

			# デバイスに描画 #
			file = open(filePath, 'rb')
			self.myEBBDevice.Write(file.read())
			file.close()

			# 指定件数を表示し続ける #
			if self.myDBMS.getRowCount(self.config_hardwareModel) <= self.config_animationHoldCount:
				print 'delete skip'
				return

			# データベースを更新 #
			if mode == self._checkMode('COUNT'):
				print 'count'
				if count <= 1:
					print '\tthen'
					self.myDBMS.removeAnimationOne(animation_id)
					os.remove(filePath)

				else:
					print '\telse'
					self.myDBMS.updateAnimation(animation_id, True, count - 1)
					self.myDBMS.commit()

			elif mode == self._checkMode('ENDTIME'):
				if end_time < time.time():
					self.myDBMS.removeAnimationOne(animation_id)
					os.remove(filePath)

		except Exception, ex:
			print >> sys.stderr, ex
			self.close()
			sys.exit(1)


	def _checkMode(self, mode = 'LOOP'):
		"""モードの入力値を正規化"""
		valid_modes = ('LOOP', 'COUNT', 'ENDTIME')

		for index, vmode in enumerate(valid_modes):
			if vmode == mode.upper():
				return index

		return 0


	def _fileWrite(self, file, image):
		"""デバイス用のバイナリをファイルに書き込み"""
		self.myEBBDevice.Draw(data = image, width = self.model_widthPixel, height = self.model_heightPixel)
		file.write(self.myEBBDevice.ToString())
		self.myEBBDevice.Reset()

